# backend/app/ai/views.py
print("[DEBUG] ai/views.py loaded")
import os
import uuid
import shutil
import asyncio
import threading
from django.http import JsonResponse, HttpResponse, FileResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from django.conf import settings
from typing import Dict, Any
import io
import wave
from django.http import HttpResponse, FileResponse, StreamingHttpResponse

# 为了导入 demo 模块，扩展 sys.path
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
REALTIME_DEMO_PATH = os.path.join(HERE, "realtime_demo")
if REALTIME_DEMO_PATH not in sys.path:
    sys.path.insert(0, REALTIME_DEMO_PATH)

# 现在可以导入 demo 中的模块（确保你已把 demo 文件放到 realtime_demo/）
try:
    from audio_manager import DialogSession  # type: ignore
    import config as demo_config  # type: ignore
except Exception as e:
    DialogSession = None  # type: ignore
    demo_config = None  # type: ignore

SESSIONS_DIR = os.path.join(HERE, "sessions")
os.makedirs(SESSIONS_DIR, exist_ok=True)

def _session_fs_path(session_id: str) -> str:
    p = os.path.join(SESSIONS_DIR, session_id)
    os.makedirs(p, exist_ok=True)
    return p

@method_decorator(csrf_exempt, name='dispatch')
class AiHealthView(View):
    def get(self, request):
        return JsonResponse({"status": "ok", "ai": "realtime_demo"})

@method_decorator(csrf_exempt, name='dispatch')
class AiStartView(View):
    """
    POST -> 创建一个 session，返回 session_id。
    该接口仅用于管理会话 id；真正的音频上载通过 upload_audio 调用。
    """
    def post(self, request):
        session_id = str(uuid.uuid4())
        # 在文件系统创建会话目录
        _session_fs_path(session_id)
        return JsonResponse({"session_id": session_id, "status": "created"})

@method_decorator(csrf_exempt, name='dispatch')
class AiUploadAudioView(View):
    """
    上传音频文件（form-data, field name 'audio'），后端使用 demo 的 audio_file 模式处理。
    支持 wav 或 raw pcm。接口会同步（阻塞）直到 demo 会话完成或超时（prototype）。
    """
    def post(self, request):
        print("[DEBUG] AiUploadAudioView.post called")
        if DialogSession is None:
            return JsonResponse({"error": "Realtime demo not available on server"}, status=500)

        session_id = request.POST.get("session_id")
        if not session_id:
            return JsonResponse({"error": "session_id is required"}, status=400)

        if 'audio' not in request.FILES:
            return JsonResponse({"error": "audio file is required (form field 'audio')"}, status=400)
        f = request.FILES['audio']
        # 保存上传文件到会话文件夹
        session_path = _session_fs_path(session_id)
        input_path = os.path.join(session_path, f.name)
        with open(input_path, "wb") as wf:
            for chunk in f.chunks():
                wf.write(chunk)

        # 启动 demo 的 DialogSession 的 audio_file 模式在专用 asyncio 事件循环运行（阻塞等待）
        loop = asyncio.new_event_loop()

        result = {"status": "failed", "detail": None}

        async def _run_demo():
            import traceback
            print("[DEBUG] Entered _run_demo()")
            try:
                print("[DEBUG] Creating DialogSession...")
                ds = DialogSession(
                    ws_config=demo_config.ws_connect_config,
                    output_audio_format=demo_config.start_session_req["tts"]["audio_config"]["format"],
                    audio_file_path=input_path,
                    mod="audio_file",
                    recv_timeout=demo_config.start_session_req["dialog"]["extra"].get("recv_timeout", 10)
                )
                print(f"[DEBUG] Starting DialogSession with file={input_path}")
                await ds.start()
                demo_out = os.path.join(os.getcwd(), "output.pcm")
                print(f"[DEBUG] Checking for output.pcm at {demo_out}")
                if os.path.exists(demo_out):
                    print(f"[DEBUG] Moving output.pcm -> {session_path}")
                    shutil.move(demo_out, os.path.join(session_path, "output.pcm"))
                else:
                    print("[DEBUG] No output.pcm generated by demo.")
                result["status"] = "ok"
            except Exception as e:
                tb = traceback.format_exc()
                print("[ERROR] Exception while running demo:")
                print(tb)
                result["status"] = "error"
                result["detail"] = tb

        # 在线程中运行事件循环，以免阻塞 Django 主线程（APIFOX 会等待这个请求返回）
        # 在主线程直接运行 asyncio 事件循环，执行 demo
        try:
            asyncio.set_event_loop(loop)
            print("[DEBUG] Running _run_demo() now...")
            loop.run_until_complete(_run_demo())
        finally:
            loop.close()

        print(f"[DEBUG] _run_demo() finished with result={result}")

        # 根据结果返回
        if result.get("status") != "ok":
            return JsonResponse({"status": "error", "detail": result.get("detail")}, status=500)
        return JsonResponse({"status": "ok", "message": "audio processed", "session_id": session_id})

        if result.get("status") != "ok":
            return JsonResponse({"status": "error", "detail": result.get("detail")}, status=500)
        return JsonResponse({"status": "ok", "message": "audio processed", "session_id": session_id})

@method_decorator(csrf_exempt, name='dispatch')
class AiSendTextView(View):
    """
    POST JSON: { session_id: str, text: str } -> demo 的 chat_text_query 模式（text 模式）
    注意：demo 的 chat_text_query 依赖 WebSocket 连接的生命周期，这里仅做同步触发示例（prototype）
    """
    def post(self, request):
        import json
        body = json.loads(request.body.decode("utf-8") or "{}")
        session_id = body.get("session_id")
        text = body.get("text", "")
        if not session_id or not text:
            return JsonResponse({"error": "session_id and text are required"}, status=400)
        # 简单实现：使用 DialogSession 的 text 模式进行一次连接并发送一条 text query
        loop = asyncio.new_event_loop()
        res = {"status": "failed", "detail": None}
        async def _run_one_text():
            try:
                ds = DialogSession(ws_config=demo_config.ws_connect_config,
                                   output_audio_format=demo_config.start_session_req["tts"]["audio_config"]["format"],
                                   audio_file_path="",
                                   mod="text",
                                   recv_timeout=demo_config.start_session_req["dialog"]["extra"].get("recv_timeout", 10))
                await ds.client.connect()
                await ds.client.chat_text_query(text)
                # 等一点时间接收回复并保存 output buffer
                await asyncio.sleep(2)
                # finish
                await ds.client.finish_session()
                await ds.client.finish_connection()
                await ds.client.close()
                # demo 不保证写文件，这里我们不保存 output
                res["status"] = "ok"
            except Exception as e:
                res["detail"] = str(e)
        try:
            asyncio.set_event_loop(loop)
            loop.run_until_complete(_run_one_text())
        finally:
            loop.close()
        if res.get("status") != "ok":
            return JsonResponse({"status": "error", "detail": res.get("detail")}, status=500)
        return JsonResponse({"status": "ok", "message": "text sent"})

@method_decorator(csrf_exempt, name='dispatch')
class AiOutputDownloadView(View):
    """
    GET: /output/<session_id>/ -> 下载 output.pcm（如果存在）
    """
    def get(self, request, session_id):
        session_path = _session_fs_path(session_id)
        out_path = os.path.join(session_path, "output.pcm")
        if not os.path.exists(out_path):
            return JsonResponse({"error": "output not found"}, status=404)
        # 返回二进制文件
        return FileResponse(open(out_path, "rb"), as_attachment=True, filename=f"{session_id}_output.pcm")

# ---------- helper: 将裸 PCM 转为 WAV bytes ----------
import io
import wave
import numpy as np

def pcm_to_wav_bytes(pcm_path: str, sample_rate: int = 24000, channels: int = 1) -> bytes:
    """
    将 float32 PCM 文件转为带 WAV 头的字节流。
    """
    bio = io.BytesIO()

    # 读取 PCM float32 数据
    with open(pcm_path, "rb") as f:
        pcm_data = f.read()
    float_data = np.frombuffer(pcm_data, dtype=np.float32)

    # 转换为 int16 （WAV 常用格式）
    int16_data = np.int16(float_data * 32767)

    # 写入 WAV 文件头
    with wave.open(bio, "wb") as w:
        w.setnchannels(channels)
        w.setsampwidth(2)  # 16-bit
        w.setframerate(sample_rate)
        w.writeframes(int16_data.tobytes())

    bio.seek(0)
    return bio.read()


# ---------- View: 返回 WAV 格式流供前端直接播放 ----------
@method_decorator(csrf_exempt, name='dispatch')
class AiPlayView(View):
    """
    GET /api/ai/play/<session_id>/
    -> 返回 audio/wav 二进制流（由 float32 PCM 转换而来，可直接在浏览器播放）
    """
    def get(self, request, session_id):
        session_path = _session_fs_path(session_id)
        pcm_path = os.path.join(session_path, "output.pcm")
        if not os.path.exists(pcm_path):
            return JsonResponse({"error": "output not found"}, status=404)

        # 从 demo_config 尝试读取采样率，否则默认 24000
        try:
            sample_rate = int(
                demo_config.start_session_req["tts"]["audio_config"].get("sample_rate", 24000)
            )
        except Exception:
            sample_rate = 24000

        try:
            # 调用新版转换函数（float32 → wav）
            wav_bytes = pcm_to_wav_bytes(pcm_path, sample_rate=sample_rate, channels=1)
        except Exception as e:
            return JsonResponse({
                "error": "failed to convert pcm to wav",
                "detail": str(e)
            }, status=500)

        # 构建浏览器可播放响应
        response = HttpResponse(wav_bytes, content_type="audio/wav")
        response["Content-Disposition"] = f'inline; filename="{session_id}.wav"'
        # 可选缓存头（节省重复请求）
        response["Cache-Control"] = "public, max-age=3600"
        return response

